#include "PreCompiledHeader.h"
#include "Server.h"

unsigned Server::gameID = 0;

Server::Server()
	: mNumPlayer(0), mStartGame(false)
{
	InitializeCriticalSection(&mCSLockServer);
}


Server::~Server(void)
{
	std::vector<Player*>::iterator player_iter = mPlayers.begin();
	std::vector<Player*>::iterator player_end = mPlayers.end();

	while(player_iter != player_end)
	{
		delete *player_iter;
		++player_iter;
	}
	mPlayers.clear();

	if(mServerSocket)
		delete mServerSocket;
	mServerSocket = 0;

	//TerminateThread(

	WSACleanup();

}

bool Server::Initialize(unsigned port_num)
{
	mPortNum = port_num;
	// Server set up winsocks
	std::cout << "Server started..." << std::endl;

	// Winsock v2v2 Startup
	WSADATA wsaData;
	int result = WSAStartup(MAKEWORD(2,2), &wsaData);

	if(result)
	{
		std::cerr << "WSASTartup failed." << std::endl;
		return false;
	}
	else
	{
		std::cout << "WSAStartup succeeded: Verison " <<
			int(LOBYTE(wsaData.wVersion)) << "." <<
			int(HIBYTE(wsaData.wVersion)) << std::endl;
	}

	mServerSocket = new TSSocket();

	if( !mServerSocket->SetUpUDPServerAddress(mPortNum) )
		return false;

	DWORD threadHandle;
	HANDLE server_thread = CreateThread(NULL, 
										0, 
										&Server::ReceivingThread, 
										this, 
										0, 
										&threadHandle);

	return true;
}

unsigned short Server::GetNumOfPlayers(void)
{
	return mNumPlayer;
}

DWORD WINAPI Server::ReceivingThread(void* param)
{
	Server *server = (Server*)(param);
	// Maximum size for each buffer we are receiving
	char datareceived[sizeof(ClientTask)];
	// Number of bytes received in our buffer
	int numbytes;
	// Result determines if receving succeed or failed
	bool result;
	// Represent each client's task(each step the client took)
	ClientTask client_task;

	// While server is not terminating yet
	bool loop(true);
	while(loop)
	{
		SecureZeroMemory(datareceived, sizeof(ClientTask));
		SecureZeroMemory(&client_task, sizeof(ClientTask));

		// Extract and convert the buffer
		if (!server->mServerSocket) // may not be entirely thread-safe
			return 0;

		result = server->mServerSocket->RecvUDPData(datareceived, numbytes);
		if (!result)
			continue;

		std::memcpy(reinterpret_cast<char*>(&client_task), datareceived, sizeof(ClientTask));

		// If server already has 4 players, server stop receiving player into the network
		if(server->mNumPlayer < 4 && client_task.type == CONNECT)
		{
			// Increment the counter for new player. It will represent their unique ID.
			++server->mNumPlayer;

			// A new player created in the network
			Player *new_player = new Player;

			// Store the ip address and port number of this new client
			new_player->SetIPAddress(inet_ntoa(server->mServerSocket->fromaddr.sin_addr));
			new_player->SetPortNum(htons(server->mServerSocket->fromaddr.sin_port));
			new_player->SetUsername(client_task.buffer);
			new_player->SetGameID(gameID);
			new_player->setID(server->mNumPlayer);
			server->mPlayers.push_back(new_player);

			
			std::cout << "New Client no.: " << new_player->GetID() << std::endl;

			// send id
			GameTask game_task;
			game_task.type = CONNECT;
			game_task.id = new_player->GetID();

			server->mServerSocket->SendUDPData( reinterpret_cast<char*>(&game_task), 
												sizeof(game_task), 
												inet_ntoa(server->mServerSocket->fromaddr.sin_addr), 
												htons(server->mServerSocket->fromaddr.sin_port));

			if(server->mNumPlayer == 4)
			{
				// send id
				GameTask game_task;
				game_task.type = BEGIN;

				for(unsigned i = 0; i < 4; ++i)
				{
					server->mServerSocket->SendUDPData( reinterpret_cast<char*>(&game_task), 
														sizeof(game_task), 
														server->mPlayers[i]->GetIPAddress(),
														server->mPlayers[i]->GetPortNum());
				}
			}
		}
		// Capture all incoming data, push data to game thread for handling
		else
		{
			//GameTask *task = (GameTask *)&client_task;
			server->mQueue.Push(client_task);
		}
	}

	return 1;
}